package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Student struct {
	Id    int16
	Name  string
	Score float64
	Sex   string
	//DeletedAt gorm.DeletedAt
}

//对数据库的写操作
func main() {
	dsn1 := "root:root@tcp(127.0.0.1:3306)/userdb?charset=utf8mb4&parseTime=True&loc=Local"
	gormDB, err := gorm.Open(mysql.Open(dsn1), &gorm.Config{})

	if err != nil {
		fmt.Println("gorm.Open Error :", err)
	}

	err = gormDB.Migrator().AddColumn(&Student{}, "deletedAt")
	if err != nil {
		fmt.Println("创建列失败,error:", err)
	}

	stu := &Student{
		Name:  "fxm",
		Score: 99.99,
		Sex:   "女",
	}
	//插入一条数据
	result := gormDB.Create(stu)

	fmt.Println("插入行数：", result.RowsAffected, "插入失败原因：", result.Error)

	//返回主键id
	fmt.Println("插入后id是：", stu.Id)

	//批量插入数据
	stuslice := []*Student{{
		Name:  "fxl",
		Score: 99.99,
		Sex:   "女",
	},
		{
			Name:  "fzx",
			Score: 99.99,
			Sex:   "男",
		},
	}
	result = gormDB.Create(stuslice)
	fmt.Println("插入行数：", result.RowsAffected, "插入失败原因：", result.Error)

	//选择字段插入Select
	stu = &Student{
		Name: "lll",
		Sex:  "女",
	}
	result = gormDB.Select("Name", "Sex").Create(stu)
	fmt.Println("插入行数：", result.RowsAffected, "插入失败原因：", result.Error)

	//排除字段插入数据Omit
	stu = &Student{
		Name:  "www",
		Sex:   "女",
		Score: 10.0,
	}
	result = gormDB.Omit("Score").Create(stu)
	fmt.Println("插入行数：", result.RowsAffected, "插入失败原因：", result.Error)

	//根据 map 单条创建
	stuMap := map[string]interface{}{
		"Name":  "aaa",
		"Score": 22.22,
		"Sex":   "?",
	}
	result = gormDB.Model(&Student{}).Create(stuMap)
	fmt.Println("插入行数：", result.RowsAffected, "插入失败原因：", result.Error)

	//根据 map 批量创建
	stusMap := []map[string]interface{}{
		{
			"Name":  "apple",
			"Score": 20,
			"Sex":   "boy",
		},
		{
			"Name":  "pear",
			"Score": 21,
			"Sex":   "girl",
		},
	}
	gormDB.Model(&Student{}).Create(stusMap)
	//需要注意的是，根据 map 创建，不会自动填充 gorm.Model 结构体定义的字段。

	/*
		默认值
		可以使用 GORM 标签 default 设置默认值，插入数据时，设置的默认值会被用于填充值为零值的字段。
		需要注意的是，如果默认值本身是数据类型的零值，将不会被保存到数据库。对于数据库表的设置默认值的字段，需要预先在声明模型的 struct 字段上使用标签 default 设置默认值，否则会插入该字段数据类型的零值。
	*/
	//按默认值插入
	stu = &Student{}
	result = gormDB.Create(stu)
	fmt.Println("插入行数：", result.RowsAffected, "插入失败原因：", result.Error)

	//单条删除
	//删除单条记录，删除对象需要指定主键，否则会触发批量删除并且会被阻止全局删除功能拦截。
	stu = &Student{}
	stu.Id = 4
	gormDB.Delete(&stu)

	//根据主键删除
	//GORM 支持根据内联条件指定删除对象的主键，但是只支持数据类型为整型主键。
	gormDB.Delete(&Student{}, 2)
	//gormDB.Delete(&Student{}, "2")
	gormDB.Delete(&Student{}, []int{8, 9})

	//批量删除
	//GORM 可以根据指定的删除条件，批量删除全部匹配的记录。
	gormDB.Where("name LIKE ?", "%f%").Delete(Student{})
	gormDB.Delete(Student{}, "sex > ?", 99)

	//阻止全局删除;如果没有指定 WHERE 条件，GORM 不会执行删除操作，并返回 ErrMissingWhereClause 错误。
	gormDB.Delete(Student{}) //如果想要全局删除，必须指定 WHERE 条件，或者执行原生 SQL，或者启用 AllowGlobalUpdate 模式。
	//没有where情况下 会报错~！

	/*
		软删除
		如果模型包含 gorm.DeletedAt 字段，将会启用软删除，软删除是指不会真的删除记录，而是会将 DeletedAt 字段设置为当前时间，并且被软删除的记录，不可以通过正常查询操作获取。
		如果需要查询被软删除的记录，需要使用 Unscoped 方法。
		永久删除
		永久删除记录，也需要使用 Unscoped 方法。
	*/
	gormDB.Unscoped().Where("id = ?", 5).Delete(&Student{})
	gormDB.Unscoped().Where("id IN ?", []int{8, 9}).Delete(&Student{})

	//更新操作
	//保存所有字段
	//调用 Save 方法更新数据，会保存所有字段，即使字段的值为字段类型的零值。
	student := Student{}
	gormDB.First(&student)
	student.Name = "cat1"
	student.Sex = "bbbb"
	student.Score = 11.11
	gormDB.Save(&student)

	//更新单个列
	//使用 Update 方法更新单个列时，需要指定条件，否则会返回 ErrMissingWhereClause 的错误。
	student = Student{}
	student.Id = 18
	gormDB.Model(&student).Update("sex", "ccc") //根据主键Id = 18来进行的
	gormDB.Model(&Student{}).Where("score > ?", 20).Update("score", 100)

	//更新多个列
	//使用 Updates 方法更新多个列，GORM 支持 struct 和 map[string]interface{} 参数，需要注意的是，当使用 struct 作为参数时，GORM 只会更新字段的值不是字段类型的零值的字段。

	//struct

	student = Student{}
	student.Id = 1
	gormDB.Model(&student).Updates(Student{Name: "book", Score: 22})
	//map
	student = Student{}
	student.Id = 19 //根据主键设置新值
	gormDB.Model(&student).Updates(map[string]interface{}{"name": "panda", "score": 30})

	//更新选定或排除字段
	//更新操作，也支持根据选定或排除字段进行更新。
	//选定字段 Select
	student = Student{}
	student.Id = 14
	gormDB.Model(&student).Select("name", "sex").Updates(map[string]interface{}{"name": "lucy", "sex": "lm"})
	//排除字段 Omit
	student = Student{}
	student.Id = 3
	gormDB.Model(&student).Omit("name").Updates(map[string]interface{}{"name": "dog", "score": 29, "sex": "dm"})

	//批量更新
	//如果未通过 Model 方法指定记录的主键，GORM 则会执行批量更新。
	//struct
	gormDB.Model(&Student{}).Where("name=?", "lll").Updates(Student{Name: "tom", Score: 18})
	//map
	gormDB.Model(&Student{}).Where("name = ?", "apple").Updates(map[string]interface{}{"name": "milk", "sex": "om"})
	gormDB.Table("students").Where("name = ?", "pear").Updates(map[string]interface{}{"name": "honey", "sex": "om"})

	//阻止全局更新
	//如果未指定任何条件执行批量更新，默认情况下，GORM 不会执行该操作，并且会返回 ErrMissingWhereClause 错误。
	//如果希望执行全局更新，需要指定条件，或使用原生 SQL，或启用 AllowGlobalUpdate 模式。
	//更新的记录数和更新操作的错误
	//获取受影响的行数和更新操作的错误。
	result = gormDB.Table("students").Where("name = ?", "honey").Updates(map[string]interface{}{"name": "life", "sex": "qweqweqew"})
	rows := result.RowsAffected
	fmt.Printf("更新的记录数：%d\n", rows)
	fmt.Println(result.Error)
	//测试阻止全局更新  没有where情况下 会报错~！
	gormDB.Model(&Student{}).Updates(Student{Name: "tom", Score: 18})
}
